package br.cin.stadium.middleware.directory;

import java.util.ArrayList;

import br.cin.stadium.middleware.StadiumException;
import br.cin.stadium.middleware.event.Event;
import br.cin.stadium.middleware.event.EventCallbackReceiver;
import br.cin.stadium.middleware.event.EventManager;
import br.cin.stadium.middleware.event.EventMiddleware;
import br.cin.stadium.middleware.marshall.Marshaller;
import br.cin.stadium.middleware.media.MediaReference;
import br.cin.stadium.middleware.network.config.NetConfig;
import br.cin.stadium.middleware.util.Logger;

/**
 * This class provides methods to BIND and UNBIND this device into Service Directory Server.
 * @author Fabiano, Jorge, Rafael and Vando
 *
 */
public class StadiumDirectory implements EventCallbackReceiver {
	public static final byte BIND_CODE   = 1;
	public static final byte UNBIND_CODE = 2;

	/**
	 * Unique instance for this class - Singleton Pattern
	 */
	private static StadiumDirectory instance;
	
	/**
	 * This device IP
	 */
	private String myIP = null;
	
	/**
	 * Get Unique Stadium Directory instance - Singleton Pattern
	 * 
	 * @return StadiumDirectory unique instance
	 */
	public static StadiumDirectory getInstance() {
		/* colocar a descoberta do servidor wifi */
		if(instance == null) {
			try {
				instance = new StadiumDirectory();
			} catch (StadiumException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		return instance;
	}

	/**
	 * Constructor - Responsible to send get IP event.
	 * @throws StadiumException 
	 */
	private StadiumDirectory() throws StadiumException {
		EventMiddleware e = new EventMiddleware(-1, Event.OPCODE_GET_IP);
		EventManager.getInstance().requestAction(e, this);

		if(this.myIP == null) {
			Logger.log("MY IP is NULL");
			synchronized (this) {
				try {
					Logger.log("Calling WAIT()");
					wait();
				} catch (InterruptedException ie) {
					Logger.logError("InterruptedException", ie);
					throw new StadiumException("Error - StadiumDirectory(Constructor)-->MyIP is null");
				}
			}
		}
		Logger.log("APOS WAIT ==> " + this.myIP);
	}

	/**
	 * Register the device into Directory Service.
	 * 
	 * @throws StadiumException If an error occurred.
	 */
	public void bind() throws StadiumException {
		Logger.log("StadiumDirectory", "bind", "Begin");

		
		ServerLocation slConn = new ServerLocation(NetConfig.STADIUM_DIRECTORY_IP,NetConfig.STADIUM_DIRECTOR_PORT_REGISTER);
		ServerLocation slMyIp = new ServerLocation(this.myIP, NetConfig.STADIUM_PORT_CLIENT_LISTEN);
		
		EventMiddleware e = new EventMiddleware(-1, Event.OPCODE_BIND, new Object[]{slConn, slMyIp}, new byte[]{BIND_CODE});
		EventManager.getInstance().doAction(e);

		
		Logger.log("StadiumDirectory", "bind", "End");
	}

	/**
	 * UNregister the device into Directory Service.
	 * 
	 * @throws StadiumException If an error occurred.
	 */
	public void unBind() throws StadiumException {
		Logger.log("StadiumDirectory", "unBind", "End");

		ServerLocation slConn = new ServerLocation(NetConfig.STADIUM_DIRECTORY_IP,NetConfig.STADIUM_DIRECTOR_PORT_REGISTER);
		ServerLocation slMyIp = new ServerLocation(this.myIP, NetConfig.STADIUM_PORT_CLIENT_LISTEN);
		
		EventMiddleware e = new EventMiddleware(-1, Event.OPCODE_UNBIND, new Object[]{slConn, slMyIp}, new byte[]{UNBIND_CODE});
		EventManager.getInstance().doAction(e);
		
		Logger.log("StadiumDirectory", "unBind", "End");
	}

	/**
	 * Send an Retrieve_Devices to retrieve all devices references.
	 * @param receiver "Who" will handle the event response.
	 */
	public void retrieve(EventCallbackReceiver receiver) {
		Logger.log("StadiumDirectory", "retrieve", "Begin");
		
		if(receiver == null) {
			receiver = this;
		}
		
		EventMiddleware e = new EventMiddleware(-1, Event.OPCODE_RETRIEVE_DEVICES, new String[]{NetConfig.STADIUM_DIRECTORY_IP, NetConfig.STADIUM_DIRECTOR_PORT_RETRIEVE+""});
		EventManager.getInstance().requestAction(e, receiver);

		Logger.log("StadiumDirectory", "retrieve", "End");

	}
	
	public void notifyDevices(MediaReference ref) {
		Logger.log("StadiumDirectory", "notifyDevices", "Begin");
		
		EventMiddleware e = new EventMiddleware(-1, Event.OPCODE_RETRIEVE_DEVICES, new Object[]{NetConfig.STADIUM_DIRECTORY_IP, NetConfig.STADIUM_DIRECTOR_PORT_RETRIEVE+"", ref});
		EventManager.getInstance().requestAction(e, this);

		Logger.log("StadiumDirectory", "notifyDevices", "End");
		
	}

	/* (non-Javadoc)
	 * @see br.cin.stadium.middleware.event.EventCallbackReceiver#handleEvent(br.cin.stadium.middleware.event.Event)
	 */
	public void handleEvent(EventMiddleware event) {
		Logger.log("StadiumDirectory","handleEvent","Begin");
		
		switch(event.getOpCode()) {
			case Event.OPCODE_GET_IP:
				this.myIP = new String(event.getData());
				Logger.log("IP = " + this.myIP);
				
				synchronized (this) {
					notifyAll();
				}

				Logger.log("Notify ALL Called");
				break;
			case Event.OPCODE_RETRIEVE_DEVICES:

				//Get Devices/ServerLocation
				byte[] data = event.getData();
				ServerLocation template = new ServerLocation();
				
				ArrayList<ServerLocation> devices = new ArrayList<ServerLocation>();
				Marshaller.unmarshall(template, devices, data);
				
				//There are more devices than me
				if(devices.size() > 1) {
					Object[] params = devices.toArray();
					
					//Get Data to Send - Encapsulated in Parameter
					byte[] dataToSend = Marshaller.marshall((MediaReference)event.getParameter()[2]);
					
					//Send New Write to Devices
					EventMiddleware e = new EventMiddleware(-1, Event.OPCODE_NEW_WRITE, params, dataToSend);
					EventManager.getInstance().requestAction(e, null);
				} else {
					Logger.log("Only one devices in Directory - probably this unit/device");
				}
				
				
				break;
		}

		Logger.log("StadiumDirectory","handleEvent","End");
	}
	
	public String getMyIP() {
		return myIP;
	}



	
}